import sys
from os.path import abspath, pardir, join, basename

from PyQt6.QtCore import QPointF, Qt, QSize, QRectF
from PyQt6.QtGui import QIcon, QAction, QFont, QPixmap, QPainter
from PyQt6.QtWidgets import QMainWindow, QApplication, QLabel, QGraphicsItem, \
    QInputDialog, QColorDialog, QGraphicsEllipseItem, QGraphicsRectItem, QGraphicsLineItem, QGraphicsTextItem, \
    QMessageBox, QFileDialog

from DrawStyle import DrawStyle
from shape import Shapes, GraphData
from ui.DrawBoard_ui import Ui_MainWindow
from widget import Widgets
from widget.BoardGraphicsView import BoardGraphicsView, BoardGraphicsScene
from widget.ShapeButton import ShapeButton


class DrawBoard(QMainWindow, Ui_MainWindow):

    def __init__(self, parent=None):
        super(DrawBoard, self).__init__(parent)
        self.setupUi(self)
        self.setWindowIcon(QIcon('image/drawboard.jpg'))
        self.setWindowFlag(Qt.WindowType.MSWindowsFixedSizeDialogHint)
        # 变量
        self.currentFile = 'untitled'           # 当前正在编辑的文件名
        self.currentPath = None                 # 当前正在编辑的文件路径
        self.zIndex = 0                         # 下一个图元的叠放次序
        self.item: QGraphicsItem | None = None  # 选中的图元
        # 界面初始化
        self.initFilebar()
        self.initStylebar()
        self.initPainterbar()
        self.initBoard()

    def initFilebar(self):
        """
        初始化文件栏
        """
        self.tbrFile.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonIconOnly)
        self.tbrFile.setIconSize(QSize(51, 51))
        self.tbrFile.layout().setContentsMargins(5, 5, 5, 5)
        self.tbrFile.layout().setSpacing(20)
        self.tbNew = QAction(QIcon('image/new.jpg'), '新建')
        self.tbNew.triggered.connect(self.newFile)
        self.tbOpen = QAction(QIcon('image/open.jpg'), '打开')
        self.tbOpen.triggered.connect(self.openFile)
        self.tbSave = QAction(QIcon('image/save.jpg'), '保存')
        self.tbSave.triggered.connect(lambda: self.saveFile(self.currentPath))
        self.tbSaveAs = QAction(QIcon('image/saveaspic.jpg'), '导出')
        self.tbSaveAs.triggered.connect(self.exportImage)
        self.tbrFile.addActions((self.tbNew, self.tbOpen, self.tbSave, self.tbSaveAs))

    def initStylebar(self):
        """
        初始化样式栏
        """
        self.tbrStyle.layout().setContentsMargins(5, 5, 5, 5)
        self.tbrStyle.layout().setSpacing(10)
        font = QFont('仿宋', 14)
        # ---
        label = Widgets.label('样式栏', font)
        self.tbrStyle.addWidget(label)
        self.tbrStyle.addSeparator()
        # 填充色
        self.pbBrushColor = Widgets.colorButton('填充', 0, 255, 0)
        self.pbBrushColor.clicked.connect(lambda: self.selectColor('brush'))
        self.tbrStyle.addWidget(self.pbBrushColor)
        self.tbrStyle.addSeparator()
        # 线宽/线型
        self.cobPenWidth = Widgets.imageComboBox('线条粗细', 'pw2', 'pw4', 'pw6')
        self.cobPenWidth.currentIndexChanged[int].connect(lambda i: setattr(DrawStyle.activeStyle(), 'penwidth', i))
        self.tbrStyle.addWidget(self.cobPenWidth)
        self.cobPenStyle = Widgets.imageComboBox('线条类型', 'pssolid', 'psdot', 'psdashdot')
        self.cobPenStyle.currentIndexChanged[int].connect(lambda i: setattr(DrawStyle.activeStyle(), 'penstyle', i))
        self.tbrStyle.addWidget(self.cobPenStyle)
        # 线条颜色
        self.pbPenColor = Widgets.colorButton('线条颜色', 0, 0, 255)
        self.pbPenColor.clicked.connect(lambda: self.selectColor('pen'))
        self.tbrStyle.addWidget(self.pbPenColor)
        self.tbrStyle.addSeparator()
        # 字体
        self.cobFont = Widgets.textComboBox('字体', font, 'Times New Roman', '微软雅黑', '华文楷体')
        self.cobFont.currentTextChanged[str].connect(lambda s: setattr(DrawStyle.activeStyle(), 'fontfamily', s))
        self.tbrStyle.addWidget(self.cobFont)
        # 字号
        self.sbFontSize = Widgets.spin('字号', font, 6, 72, 12)
        self.sbFontSize.valueChanged.connect(lambda i: setattr(DrawStyle.activeStyle(), 'fontsize', i))
        self.tbrStyle.addWidget(self.sbFontSize)
        # 加粗/倾斜
        self.pbBold = Widgets.iconButton('加粗', QIcon('image/bold.jpg'))
        self.pbBold.clicked.connect(lambda: setattr(DrawStyle.activeStyle(), 'bold', not DrawStyle.activeStyle().bold))
        self.tbrStyle.addWidget(self.pbBold)
        self.pbItalic = Widgets.iconButton('倾斜', QIcon('image/italic.jpg'))
        self.pbItalic.clicked.connect(lambda: setattr(DrawStyle.activeStyle(), 'italic', not DrawStyle.activeStyle().italic))
        self.tbrStyle.addWidget(self.pbItalic)
        # 文字颜色
        self.pbTextColor = Widgets.colorButton('文字颜色', 0, 0, 0)
        self.pbTextColor.clicked.connect(lambda: self.selectColor('text'))
        self.tbrStyle.addWidget(self.pbTextColor)
        self.tbrStyle.addSeparator()
        # 放大/缩小
        self.pbZoomIn = Widgets.iconButton('放大', QIcon('image/zoomin.jpg'))
        self.pbZoomIn.clicked.connect(lambda: self.scaleGraphic(0.1))
        self.tbrStyle.addWidget(self.pbZoomIn)
        self.pbZoomOut = Widgets.iconButton('缩小', QIcon('image/zoomout.jpg'))
        self.pbZoomOut.clicked.connect(lambda: self.scaleGraphic(-0.1))
        self.tbrStyle.addWidget(self.pbZoomOut)
        # 旋转
        self.pbRotateLeft = Widgets.iconButton('左旋', QIcon('image/rotateleft.jpg'))
        self.pbRotateLeft.clicked.connect(lambda: self.rotateGraphic(10))
        self.tbrStyle.addWidget(self.pbRotateLeft)
        self.pbRotateRight = Widgets.iconButton('右旋', QIcon('image/rotateright.jpg'))
        self.pbRotateRight.clicked.connect(lambda: self.rotateGraphic(-10))
        self.tbrStyle.addWidget(self.pbRotateRight)
        # 删除
        self.pbDelete = Widgets.iconButton('删除', QIcon('image/delete.jpg'))
        self.pbDelete.clicked.connect(self.deleteGraphic)
        self.tbrStyle.addWidget(self.pbDelete)
        # 初始化样式
        DrawStyle.activeStyle().styleChanged.connect(self.updateStyles)
        self.updateStyles(False)

    def initPainterbar(self):
        """
        工具箱
        """
        self.tbrShape.layout().setContentsMargins(10, 10, 10, 10)
        self.tbrShape.layout().setSpacing(20)

        self.pbEllipse = ShapeButton(QIcon('image/ellipse.jpg'))
        self.pbEllipse.released.connect(self.drawEllipse)
        self.tbrShape.addWidget(self.pbEllipse)
        self.pbRect = ShapeButton(QIcon('image/rect.jpg'))
        self.pbRect.released.connect(self.drawRect)
        self.tbrShape.addWidget(self.pbRect)
        self.pbLine = ShapeButton(QIcon('image/line.jpg'))
        self.pbLine.released.connect(self.drawLine)
        self.tbrShape.addWidget(self.pbLine)
        self.pbText = ShapeButton(QIcon('image/text.jpg'))
        self.pbText.released.connect(self.drawText)
        self.tbrShape.addWidget(self.pbText)

    def initBoard(self):
        """
        绘图区
        :return:
        """
        self.gvBoard = BoardGraphicsView(self.centralwidget)
        self.gvBoard.setGeometry(0, 0, 1000, 600)
        w = self.gvBoard.width() - 2
        h = self.gvBoard.height() - 2
        # 设置绘图区左上角坐标
        rect = QRectF(-(w / 2), -(h / 2), w, h)
        self.scene = BoardGraphicsScene(rect)
        self.gvBoard.setScene(self.scene)
        # 右对齐
        self.lbStatus = QLabel('图元文件: %s' % self.currentFile)
        self.statusbar.addPermanentWidget(self.lbStatus)
        self.gvBoard.mousemoved.connect(self.updateStatus)
        self.gvBoard.selectionChanged.connect(self.selectGraphic)
        self.gvBoard.clearSelection.connect(lambda: self.selectGraphic(None))

    def updateStyles(self, select):
        # 填充色
        Widgets.setButtonColor(self.pbBrushColor, DrawStyle.activeStyle().brushcolor)
        # 线宽/线型
        self.cobPenWidth.setCurrentIndex(int(DrawStyle.activeStyle().penwidth / 2 - 1) % 3)
        self.cobPenStyle.setCurrentIndex({Qt.PenStyle.SolidLine: 0,
                                          Qt.PenStyle.DotLine: 1,
                                          Qt.PenStyle.DashDotLine: 2}[DrawStyle.activeStyle().penstyle])
        # 线条颜色
        Widgets.setButtonColor(self.pbPenColor, DrawStyle.activeStyle().pencolor)
        # 字体
        self.cobFont.setCurrentText(DrawStyle.activeStyle().fontfamily)
        # 字号
        self.sbFontSize.setValue(DrawStyle.activeStyle().fontsize)
        # 加粗/倾斜
        self.pbBold.setFlat(DrawStyle.activeStyle().bold)
        self.pbItalic.setFlat(DrawStyle.activeStyle().italic)
        # 文字颜色
        Widgets.setButtonColor(self.pbTextColor, DrawStyle.activeStyle().textcolor)

        if not select and self.item is not None:
            if isinstance(self.item, QGraphicsEllipseItem) or isinstance(self.item, QGraphicsRectItem):
                self.item.setPen(DrawStyle.activeStyle().pen)
                self.item.setBrush(DrawStyle.activeStyle().brush)
            elif isinstance(self.item, QGraphicsLineItem):
                self.item.setPen(DrawStyle.activeStyle().pen)
            elif isinstance(self.item, QGraphicsTextItem):
                self.item.setFont(DrawStyle.activeStyle().font)
                self.item.setDefaultTextColor(DrawStyle.activeStyle().fontcolor)

    def updateStatus(self, point: QPointF):
        """
        更新状态栏信息
        """
        sp = self.gvBoard.mapToScene(point.toPoint())
        self.statusbar.showMessage('场景坐标: (%d, %d)' % (sp.x(), sp.y()))
        self.lbStatus.setText('图元文件: %s' % self.currentFile)

    def selectGraphic(self, item: QGraphicsItem | None):
        """
        选中图元
        :param item: 图元
        """
        self.item = item
        DrawStyle.activeStyle().selectGraphic(item)
        if item is None:
            self.statusbar.clearMessage()
        else:
            self.statusbar.showMessage('选中 %s(%d, %d)' % (item.__class__.__name__, item.x(), item.y()), 3000)

    def newFile(self):
        """
        新建文件
        实际是清空所有数据
        """
        # 当前视图是否存在图元
        if len(self.scene.items()) > 0:
            reply = QMessageBox.question(self, '提示', '是否保存',
                                         QMessageBox.StandardButton.Save | QMessageBox.StandardButton.Discard | QMessageBox.StandardButton.Cancel)
            if reply == QMessageBox.StandardButton.Cancel:
                # 取消
                return
            elif reply == QMessageBox.StandardButton.Save:
                # 保存
                self.saveFile(self.currentPath)
        # 清除文件路径
        self.currentFile = 'untitled'
        self.currentPath = None
        # 清除场景图元
        self.gvBoard.clearSelection.emit()
        self.scene.clear()
        self.zIndex = 0

    def openFile(self):
        """
        打开文件
        """
        # 清除当前场景
        self.newFile()
        # 读取数据
        if self.currentPath is None:
            openDir = abspath('.')
        else:
            openDir = abspath(join(abspath('.'), pardir))
        path = QFileDialog.getOpenFileName(self, '打开', openDir, '二进制文件 (*.dat)')[0]
        if path != '':
            # 读取数据
            graphics = GraphData.load_graph_data(path)
            self.currentFile = basename(path)
            self.currentPath = path
            for graphic in graphics:
                self.scene.addItem(graphic)
                if graphic.zValue() >= self.zIndex:
                    self.zIndex = graphic.zValue() + 1

    def saveFile(self, path: str | None):
        """
        保存场景到文件
        :param path: 文件路径，None 表示弹窗选择
        """
        # 确定路径
        if path is None:
            if self.currentPath is None:
                defaultDir = join(abspath('.'), self.currentFile + '.dat')
            else:
                defaultDir = self.currentPath

            path = QFileDialog.getSaveFileName(self, '保存', defaultDir, '二进制文件(.dat)')[0]

        # 保存
        if path != '':
            GraphData.save_graph_data(path, self.scene.items())
            self.currentPath = path
            self.currentFile = basename(path)

    def exportImage(self):
        if self.currentPath is None:
            path = join(abspath('.'), self.currentFile + '.png')
        else:
            path = self.currentPath[:-3] + 'png'

        path = QFileDialog.getSaveFileName(self, '导出图片', path, '图片文件(*.jpg *.png *.gif *.ico *.bmp)')[0]
        if path != '':
            rect = self.gvBoard.viewport().rect()
            pixmap = QPixmap(rect.size())
            pixmap.fill(Qt.GlobalColor.white)
            painter = QPainter()
            painter.begin(pixmap)
            self.gvBoard.render(painter, QRectF(pixmap.rect()), rect)
            painter.end()
            pixmap.save(path)

    def drawEllipse(self):
        """
        绘制圆形
        """
        point = self.gvBoard.mapToScene(self.gvBoard.dropPoint.toPoint())
        item = Shapes.newCircle(point, self.zIndex)
        self.zIndex += 1
        self.scene.addItem(item)
        self.selectGraphic(item)

    def drawRect(self):
        """
        绘制矩形
        """
        point = self.gvBoard.mapToScene(self.gvBoard.dropPoint.toPoint())
        item = Shapes.newRect(point, self.zIndex)
        self.zIndex += 1
        self.scene.addItem(item)
        self.selectGraphic(item)

    def drawLine(self):
        """
        绘制直线
        """
        # 直线图元以右端点为原点
        point = self.gvBoard.mapToScene(self.gvBoard.dropPoint.toPoint())
        item = Shapes.newLine(point, self.zIndex)
        self.zIndex += 1
        self.scene.addItem(item)
        self.selectGraphic(item)

    def drawText(self):
        """
        绘制文本
        """
        text, ok = QInputDialog.getText(self, '添加文字', '请输入')
        if not ok:
            return

        point = self.gvBoard.mapToScene(self.gvBoard.dropPoint.toPoint())
        item = Shapes.newText(point, self.zIndex, text)
        self.zIndex += 1
        self.scene.addItem(item)
        self.selectGraphic(item)

    def selectColor(self, mode):
        if mode == 'brush':
            color = DrawStyle.activeStyle().brushcolor
        elif mode == 'pen':
            color = DrawStyle.activeStyle().pencolor
        elif mode == 'text':
            color = DrawStyle.activeStyle().textcolor
        else:
            color = None

        color = QColorDialog.getColor(color, self, '选择颜色')
        if color.isValid():
            if mode == 'brush':
                DrawStyle.activeStyle().brushcolor = color
            elif mode == 'pen':
                DrawStyle.activeStyle().pencolor = color
            elif mode == 'text':
                DrawStyle.activeStyle().textcolor = color

    def scaleGraphic(self, delta):
        if self.item is not None:
            self.item.setScale(self.item.scale() + delta)

    def rotateGraphic(self, delta):
        if self.item is not None:
            self.item.setRotation(self.item.rotation() + delta)

    def deleteGraphic(self):
        if self.item is not None:
            if self.item == self.zIndex:
                self.zIndex -= 1
            self.scene.removeItem(self.item)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = DrawBoard()
    main.show()
    sys.exit(app.exec())
